///////////////////////////////////////////////////////////////////////////////
// Company: University of Cincinnati
// Author: Jordan Ross, Ben Gentry, Bryan Butsch
//
// Created Date: 09/18/2013
// Design Name:
// Module Name: Proc
// Project Name: Proc
// Target Devices: Cyclone II EP2C20F484C7
// Tool Versions: Quartus 13.0
// Description: This module is a simple processor that supports 4 basic instructions.
//   The 4 instructions are as follows:
//   Operation            | Function Performed
//   ---------------------|-------------------------------
//	 MV  Reg[X],Reg[Y]    | Reg[X] <-- {Reg[Y]}
//   MVI Reg[X],#D        | Reg[X] <-- D
//   ADD Reg[X], Reg[Y]   | Reg[X] <-- {Reg[X]} + {Reg[Y]}
//   ADD Reg[X], Reg[Y]   | Reg[X] <-- {Reg[X]} - {Reg[Y]} 
//
//   The inputs to the module are the data in (DIN) line, the processor clock,
//   Run, and active low reset. The outputs of this simple processor are the Bus,
//   a done signal, as well as a 2-bit error signal.
// 
// Dependencies: regn.v
//               dec3to8.v
//               TenToOneMux.v
// 
// Revision:
// 0.01 - File Created
// 0.02 - Code compiled
// 0.03 - Added MV instruction. Compiles but not tested.
// 0.04 - Fixed next state not becoming the current state. Compiles correctly
// 0.05 - Added 9-bit Register declarations
// 0.06 - Fixed the mux. Compiles. Not tested.
// 0.06 - Fair amount of rework to the code. Added comments and MV instruction
//        no longer working because states are not changing.
// 0.07 - MV works perfectly. Changed MV to only require T1 to complete.
// 0.08 - AddSub module frame included.
// 0.09 - Addition framework in place, merge with MVI changes
// 0.10 - Added SUB instruction.
// 0.11 - Added SUB to case. ADD also added because identical to SUB in implementation
// 0.12 - Fixed ADD and SUB. Processor passes initial testing using TCL scripts.
// 0.13 - Minor code clean up. Processor passes final test bench test.
//
// Additional Comments:
//
///////////////////////////////////////////////////////////////////////////////
module Proc (DIN, Resetn, Clock, Run, Done, BusWires/*, ErrorLED*/);
	input [8:0] DIN;
	input Resetn, Clock, Run;
	output reg Done;
	output [8:0] BusWires;
	//output [1:0] ErrorLED;
	
	// Internal wire connecting the AddSub module to the G register
	wire [8:0] AddSubOut;
	
	// Timing cycles
	parameter T0 = 2'b00, T1 = 2'b01, T2 = 2'b10, T3 = 2'b11;
	
	// Instructions
	parameter MV = 3'b000, MVI = 3'b001, ADD = 3'b010, SUB = 3'b011;
	
	// Next state, and current state values
	reg [1:0] Tstep_D, Tstep_Q;
	
	////////////////////////////////////////////////////////
	// Instruction Format:
	// Bit:   | 8  |  7 |  6 |  5 |  4 |  3 |  2 |  1 |  0 |
	// Value: |I[2]|I[1]|I[0]|X[2]|X[1]|X[0]|Y[2]|Y[1]|Y[0]|
	////////////////////////////////////////////////////////
	wire [8:0] IR, A, G, R0, R1, R2, R3, R4, R5, R6, R7;
	wire [2:0] I;
	
	// 8 - bit representation of the 3-bit input given in the 
	// instruction.
	wire [7:0] Xreg, Yreg;
	
	// Select lines for the MUX
	reg Gout, DINout;
	reg [7:0] Regout;
	
	// Control bits for the 10 registers in the processor
	reg [10:0] Rin; // Reset/load lines for registers 
						// 0 -> IR
						// 1 -> Ain
						// 2 -> Gin
						// 3-10 -> Reg0-7
	
	// An Error bit that is set if something is go wrong.
	// Maybe make useful later.
	reg [1:0] Error;
	
	// Initialize the reg values
	initial begin
		Tstep_D = T0;
		Tstep_Q = T0;
		Gout = 0;
		DINout = 0;
		Regout = 'd0;
		Rin = 0;
		Error = 0;
		Done = 0;
	end
	
	// Instantiate the instruction register as regn module
	regn ir(DIN, Rin[0], Clock, IR);
	
	// Assign the top 3 bits of the IR as the instruction.
	assign I = IR[8:6];
	
	// Instantiate dec3to8 for the X and Y input
	dec3to8 decX (
		.W		(IR[5:3]),
		.En	(1'b1),
		.Y 	(Xreg)
	);
	
	dec3to8 decY (
		.W		(IR[2:0]),
		.En	(1'b1),
		.Y 	(Yreg)
	);
	
	//Instantiate the registers for R0-R7 and A and G.
	regn reg_A(
		.R			(BusWires),
		.Rin		(Rin[1]),
		.Clock	(Clock),
		.Q			(A)
	);
	
	regn reg_G(
		.R			(AddSubOut),
		.Rin		(Rin[2]),
		.Clock	(Clock),
		.Q			(G)
	);
	
	regn reg_0(
		.R			(BusWires),
		.Rin		(Rin[3]),
		.Clock	(Clock),
		.Q			(R0)
	);
	
	regn reg_1(
		.R			(BusWires),
		.Rin		(Rin[4]),
		.Clock	(Clock),
		.Q			(R1)
	);
	
	regn reg_2(
		.R			(BusWires),
		.Rin		(Rin[5]),
		.Clock	(Clock),
		.Q			(R2)
	);
	
	regn reg_3(
		.R			(BusWires),
		.Rin		(Rin[6]),
		.Clock	(Clock),
		.Q			(R3)
	);
	
	regn reg_4(
		.R			(BusWires),
		.Rin		(Rin[7]),
		.Clock	(Clock),
		.Q			(R4)
	);
	
	regn reg_5(
		.R			(BusWires),
		.Rin		(Rin[8]),
		.Clock	(Clock),
		.Q			(R5)
	);
	
	regn reg_6(
		.R			(BusWires),
		.Rin		(Rin[9]),
		.Clock	(Clock),
		.Q			(R6)
	);
	
	regn reg_7(
		.R			(BusWires),
		.Rin		(Rin[10]),
		.Clock	(Clock),
		.Q			(R7)
	);
	
	
	// Instantiate the ten input mux that is controlled by 3 select lines (Gout,
	// DINout, and Regout[7:0]).
	TenToOneMux mux(Gout, DINout, Regout, R0, R1, R2, R3, R4, R5, R6, R7, DIN, G, BusWires);
	
	// Instantiate the AddSub module to perform the addition and subtraction operations
	// based on the state of I
	AddSub addsub(A, BusWires, I, AddSubOut);
		
	// Control FSM state table
	always @(Tstep_Q, Run, Done, I)
		begin
		case (Tstep_Q)
			T0: // data is loaded into IR in this time step
			begin
				if (!Run) Tstep_D = T0; // If we have not started just stay at T0
				else Tstep_D = T1;      // If we have started continue on to T1
			end
			T1:
				if (I == MV || I == MVI) // We are handling MV and MVI
				begin
					if (Done == 1)
						Tstep_D = T0;
					else
						Tstep_D = T1;
				end
				else                     // Continue on for ADD and SUB
					Tstep_D = T2;
			T2:
			begin
				// Move to the next timing cycle
				Tstep_D = T3;
			end
			T3: // Cycle back to the start
				Tstep_D = T0;
			default : Error = 'b01; // If an undefined timing, set the Error
		endcase
	end
	
	// Control FSM outputs
	always @(Tstep_Q)
		begin
		// Specify initial values
		//Error = 0;
		DINout <= 0;
		Gout <= 0;
		Regout <= 0;
		Done = 0;
		Rin <= 'b000000000; // Retain the IR		
		case (Tstep_Q)
			T0: // Store DIN in IR in time step 0
			begin
				// Set IRin to 1 so that we can load the instruction from DIN
				Rin <= 'b000000001;	
			end
			T1: //Define signals in time step 1
				begin
				case (I)
					MV : // Move the contents of Reg[Y] --> Reg[X]
						begin
							Regout <= Yreg;     // Select Reg[Y] from the MUX so it
							                    // it is on the bus and can be loaded
											    // into X.
							Rin[(IR[5:3]+3)] <= 1; // Set Reg[X] to load input
							Done = 1; //Set the done bit
						end
					MVI : // Move an 9-bit constant into Reg[X]
						begin
							Gout <= 0;
							DINout <= 1; // Select DIN to be put onto the Bus
							Rin[(IR[5:3]+3)] <= 1; // Set Reg[X] to load input
							Done = 1;
						end
					ADD, SUB:
						begin
							Regout <= Xreg; // Select Reg[X] from mux.							
							Rin[1] <= 1; // Move the contents of Reg[X] to Ain
						end
					default : Error = 'b10; // If an undefined instruction, set the Error
				endcase
				end
			T2: // Define signals in time step 2
				case (I)
					ADD, SUB:
						begin
							Rin[1] <= 0; // Unset the A register to accept a new value;
							Regout <= Yreg; // Select the register specified by the Y values in IR
							Rin[2] <= 1; // Move the result of the addition to G
						end
					default : Error = 'b10; // If an undefined instruction, set the Error
				endcase
			T3: // Define signals in time step 3
				case (I)
					ADD, SUB:
						begin
							Rin[2] <= 0; // Unset the G register to accept a new value
							Rin[(IR[5:3]+3)] <= 1; // Put the contents of Gout into R[X].
							Gout <= 1; // Select Gout from mux.
							Done = 1;
						end
					default : Error = 'b10; // If an undefined instruction, set the Error
				endcase
			default : Error = 1; // If an undefined timing cycle set the Error
		endcase
	end
	
	// Control FSM flip-flops
	always @(posedge Clock, negedge Resetn) begin
		if (!Resetn)
			Tstep_Q <= T0;      // Reset has been pressed, return back to start
		else
			Tstep_Q <= Tstep_D; // If reset has not been pressed then go to the next
			                    // state.
	end
	
	// Assign the error LED to the Error bit
	//assign ErrorLED[1:0] = Error[1:0];
	
	// Assign the bus to the bus wires
	//assign Bus[8:0] = BusWires[8:0];
		
endmodule
